// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"
#include <iostream>

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
	queue->Append((void *)currentThread);	// so go to sleep
	currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
						// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) 
{
	name=debugName;
	queue=new List;
	queueSize = 0;
	
}
Lock::~Lock() 
{
	delete queue;
}
void Lock::Acquire() 
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	if(holdingThread==NULL)
	{
		holdingThread=currentThread;
	}
	else if((holdingThread)!=(currentThread))	
	{
		queueSize++;
		queue->Append((void *)currentThread);	// so go to sleep
		#ifdef USER_PROGRAM
		#ifndef NETWORK
			processTable->sleepThread();
		#endif	
		#endif
		currentThread->Sleep();
	}
	(void) interrupt->SetLevel(oldLevel);
	return;
}

void Lock::Release() 
{
	IntStatus oldLevel = interrupt->SetLevel(IntOff);
	if(holdingThread!=currentThread)
	{
		printf("%s is not the lock owner\n",currentThread->getName());	//The current thread isn't allowed to release the lock
	}
	else
	{
		queueSize--;
		holdingThread = (Thread *)queue->Remove();
    		if (holdingThread != NULL)	   // make thread ready
    		{
    			#ifdef USER_PROGRAM
    			#ifndef NETWORK
				processTable->wakeThread();
			#endif	
			#endif
			scheduler->ReadyToRun(holdingThread);
		}
	}
	(void) interrupt->SetLevel(oldLevel);
	return;
}

int Lock::getQueueSize() {
	return queueSize;
}

Condition::Condition(char* debugName) 
{
	name = debugName;
	printf("%s\n",name);
	queue = new List;
	myLock = NULL;
	waitSize=0;
}
Condition::~Condition() { }
void Condition::Wait(Lock* conditionLock) 
{ 
	
	IntStatus oldLevel = interrupt->SetLevel(IntOff); 	//Turn off Interrupts
	if(conditionLock==NULL)					//Check that we received a lock
	{
		std::cerr<<"That's not nice, NULL Lock, really?\n";
		(void)interrupt->SetLevel(oldLevel);		//Kick out a null lock
		return;
	}

	if(myLock==NULL)					//If the condition doesn't have a lock, give it the one we received
	{

		myLock = conditionLock;
	}
	else if(myLock != conditionLock)			//If we received the wrong lock
	{
		printf("Wrong lock for wait. My lock is %s, given lock is %s.\n",myLock->getName(),conditionLock->getName());
		(void)interrupt->SetLevel(oldLevel);		//Kick them out
		return;
	}
	conditionLock->Release(); //Exit the monitor
	queue->Append(currentThread);
	waitSize++;
	#ifdef USER_PROGRAM
	#ifndef NETWORK
		processTable->sleepThread();
	#endif	
	#endif
	currentThread->Sleep();
}

void Condition::Signal(Lock* conditionLock) 
{ 
	IntStatus oldLevel = interrupt->SetLevel(IntOff); 	//Turn off Interrupts
	if(waitSize<1)
	{
		(void)interrupt->SetLevel(oldLevel);		
		return;
	}
	if(myLock != conditionLock&&myLock!=NULL)
	{
		printf("Wrong Lock for CV %s\n",getName());
		(void)interrupt->SetLevel(oldLevel);	
		return;
	}
	Thread *thread = (Thread*)queue->Remove();
	#ifdef USER_PROGRAM
	#ifndef NETWORK
		processTable->wakeThread();
	#endif
	#endif
	scheduler->ReadyToRun(thread);
	waitSize--;
	if(waitSize<1)
	{
		myLock=NULL;
	}
	(void)interrupt->SetLevel(oldLevel);
	return;
}
void Condition::Broadcast(Lock* conditionLock)
{ 
	if(myLock == NULL)
	{
		printf("No People Waiting On CV\n");
		return;
	}
	else if(myLock!=conditionLock)
	{
		printf("Wrong lock for broadcast\n");
		return;
	}
	while(waitSize>0)
	{
		
		Signal(conditionLock);
	}
}
